﻿namespace Yaaf.WP7.NativeAccess.IO.Internals.HTC
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.IO.IsolatedStorage;
    using System.Linq;
    using System.Text;
    using System.Threading;

    using Yaaf.WP7.NativeAccess.ComWrapper.HTC.COM_FileRw;
    using Yaaf.WP7.NativeAccess.ComWrapper.HTC.COM_NativeAccess;
    using Yaaf.WP7.NativeAccess.Helper;

    internal class HtcFileSystemAccess : IFileSystemAccess
    {

        private HTCFileAccess access;

        public HtcFileSystemAccess()
        {
            access = new HTCFileAccess();
        }


        public void ExecuteFile(string fullName)
        {
            var extension = Path.GetExtension(fullName);

            var value =
                Yaaf.WP7.NativeAccess.Registry.Registry.GetValue(
                    WP7.NativeAccess.Registry.Registry.HkeyClassesRoot + "\\" + extension + "\\");
            var stringValue = value == null ? null : value.ToString();

            //RegistryKey key = Registry.ClassesRoot.OpenSubKey(extension);
            //string stringValue = null;
            //string str2 = null;
            //if (key != null)
            //{
            //    stringValue = key.GetStringValue();
            //    key.Close();
            //}
            if (string.IsNullOrEmpty(stringValue))
            {
                throw new InvalidOperationException("No program is associated with this extension");
            }
            //RegistryKey key2 = Registry.ClassesRoot.OpenSubKey(stringValue + @"\Shell\Open\Command");
            //string str2;
            //if (key2 != null)
            //{
            //    str2 = key2.GetStringValue();
            //    key2.Close();
            //}

            var value2 = WP7.NativeAccess.Registry.Registry.GetValue(
                WP7.NativeAccess.Registry.Registry.HkeyClassesRoot + "\\" + stringValue + @"\Shell\Open\Command\");
            var stringValue2 = value2 == null ? null : value2.ToString();

            if (string.IsNullOrEmpty(stringValue2))
            {
                throw new InvalidOperationException("No program is associated with this extension");
            }

            Shell.LaunchSessionByUri(stringValue2.Replace("%s", fullName));
        }

        public Stream OpenFile(string path, FileMode mode, System.IO.FileAccess access)
        {
            System.IO.IsolatedStorage.IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication();
            file.CreateDirectory(DeviceInfo.TempDir);

            var fullTempDir = PathInfo.ToGeneral(DeviceInfo.TempDir);
            var fileName = System.IO.Path.GetFileName(path);
            var fullTempFileName = System.IO.Path.Combine(fullTempDir, fileName);
            var tempStoragePath = System.IO.Path.Combine(DeviceInfo.TempDir, fileName);
            var pathInfo = GetPathInformation(path);

            if (pathInfo == PathInformation.File)
            {
                CopyFile(path, fullTempFileName, true);
            }
            else
            {
                IsolatedStorageFile isolated = IsolatedStorageFile.GetUserStoreForApplication();

                if (isolated.FileExists(tempStoragePath))
                {
                    isolated.DeleteFile(tempStoragePath);
                }
            }

            return new StreamWrapper(file.OpenFile(tempStoragePath, mode, access), path, fullTempFileName);
        }

        private static Exception GetException(uint errorCode)
        {
            switch (errorCode)
            {
                case 2:
                    throw new FileNotFoundException("source not found!");
                case 4:
                    return new IOException("Could not open file");
                case 5:
                    return new UnauthorizedAccessException("Access is denied.");
                case 16:
                    return new IOException("Unable to delete the directory. ");
                case 3:
                    return new DirectoryNotFoundException("Directory not found");
                case 18:
                    return new FileNotFoundException("file not found!");
                case 32:
                    return new IOException(
                        "The process can not access the file because it is used by another process. ");
                case 80:
                    return new IOException("The file exists.");
            }

            return new IOException("Unknown IO Error: " + errorCode);
        }

        private static void CheckError(uint errorCode)
        {
            if (errorCode == 0)
            {
                return;
            }

            var error = GetException(errorCode);
            error.Data["ErrorCode"] = errorCode;
            throw error;
        }

        public void CopyFile( string source, string target, bool overwrite = true)
        {
            CheckError(access.CopyFile(source, target, (uint)(overwrite ? 1 : 0)));
        }

        public void MoveFile(string path, string dest, bool overwrite)
        {
            CopyFile(path, dest, overwrite);
            DeleteFile(path);

            //var op = new Helper.ProvisionXmlFileOperations();
            //op.AddMoveOperation(path, dest, ProvisionXmlFileAttributes.Hidden | ProvisionXmlFileAttributes.ReadOnly | ProvisionXmlFileAttributes.System);
            //ProvisionXml.Provision(op.ToString());
        }

        public void DeleteFile(string target)
        {
            CheckError(access.DeleteFile(target));
        }

        public void DeleteDirectory(string fullName, bool recursive)
        {
            if (recursive)
            {
                DeleteRecursive(fullName);
            }
            else
            {
                DeleteEmptyFolder(fullName);
            }
        }

        private void DeleteRecursive(string fullName)
        {
            foreach (var subItem in EnumerateFileSystemEntries(fullName, true, true, null, true))
            {
                var itemPath = Path.Combine(fullName, subItem.Name);

                bool isFile = subItem.IsFile.HasValue ? subItem.IsFile.Value : ExistsFile(itemPath);
                if (isFile)
                {
                    DeleteFile(itemPath);
                }
                else
                {
                    DeleteRecursive(itemPath);
                }
            }

            DeleteEmptyFolder(fullName);
        }

        private void DeleteEmptyFolder(string fullName)
        {
            var op = new ProvisionXmlFileOperations();
            op.AddDeleteDirectoryOperation(fullName);
            try
            {
                Provision.ProvisionXml.Provision(op.ToString());
            }
            catch (IOException)
            {
                if (ExistsDirectory(fullName))
                {
                    throw;
                }
            }

            if (ExistsDirectory(fullName))
            {
                throw new IOException("Directory could not be deleted (maybe is is not empty)!");
            }
        }
        
        public void CreateDirectory(string fullName)
        {
            Helper.ProvisionXmlFileOperations op = new ProvisionXmlFileOperations();
            op.AddMakeDirOperation(fullName);
            
            Provision.ProvisionXml.Provision(op.ToString());

            // Give it some time to execute
            Thread.Sleep(500);

            if (!ExistsDirectory(fullName))
            {
                throw new IOException("Directory could not be created!");
            }
        }

        public bool ExistsFile(string fullName)
        {
            return GetPathInformation(fullName) == PathInformation.File;
        }

        public bool ExistsDirectory(string fullName)
        {
            return GetPathInformation(fullName) == PathInformation.Directory;
        }

        public PathInformation GetPathInformation(string fullName)
        {
            uint info;
            var errorCode = access.GetInformation(fullName, out info);
            if (errorCode == 18 || errorCode == 3)
            {
                return PathInformation.NotExistent;
            }

            CheckError(errorCode);
            switch (info)
            {
                case 0:
                    return PathInformation.NotExistent;
                case 1:
                    return PathInformation.File;
                case 2:
                    return PathInformation.Directory;
                default:
                    throw new ArgumentOutOfRangeException("fullName", "Unknown information: " + info);

            }
        }

        private IEnumerable<string> EnumerateEntries(string directory, string search, bool fullPath = true)
        {
            uint num;
            string strPath = directory.EndsWith(@"\") ? directory : (directory + @"\");
            string strSearch = string.IsNullOrEmpty(search) ? "*" : search;
            access.GetAllFilesBufferSize(strPath, strSearch, out num);
            if (num > 0)
            {
                byte[] byteBuffer = new byte[num];
                access.GetAllFileNames(strPath, strSearch, byteBuffer, num);
                char[] trimChars = new char[2];
                trimChars[1] = '\\';
                foreach (string str3 in Encoding.Unicode.GetString(byteBuffer, 0, byteBuffer.Length).TrimEnd(trimChars).Split(new char[] { '\\' }))
                {
                    yield return fullPath ? (strPath + str3) : str3;
                }
            }
        }

        public IEnumerable<FileSystemEntryStruct> EnumerateFileSystemEntries(string directory, bool includeFiles, bool includeDirectories, string search, bool fullPath = true)
        {
            if (includeDirectories && includeFiles)
            {
                return EnumerateEntries(directory, search, fullPath).Select(s => new FileSystemEntryStruct(s));
            }

            if (includeDirectories)
            {
                return
                    EnumerateEntries(directory, search, fullPath).Where(
                        s => GetPathInformation(s) == PathInformation.Directory).Select(s => new FileSystemEntryStruct(s, false));
            }

            if (includeFiles)
            {
                return
                    EnumerateEntries(directory, search, fullPath).Where(
                        s => GetPathInformation(s) == PathInformation.File).Select(s => new FileSystemEntryStruct(s, true));
            }

            return new FileSystemEntryStruct[0];
        }

        public long GetFileSize(string fullName)
        {
            uint size;
            access.GetFileSize(fullName, out size);
            return size;
        }


        
    }
}
